/*
  1. Promise 就是一个类，在执行这个类的时候需要传递一个执行器(funtion)，执行器会立即实行
  2. Promise 中有三种状态，分别是 成功(fulfilled) 失败(rejected) 等待(pending)
     状态只会发生如下改变 一旦状态被确定，就不可被更改
     pending -> fulfilled
     pending -> rejected
  3. resolve和reject函数是用来改变状态的
     resolve 将状态更改为 成功(fulfilled)
     reject  将状态更改为 失败(rejected)
  4. then方法内部去判断状态，如果状态时成功 调用成功的回调函数，如果状态时失败，调用失败的回调函数 then被定义在类原型上
  5. then成功回调有个参数，表示成功之后的值，then失败回调有个参数，表示失败之后的值
  6. 同一个promise对象下面的then方法是可以被调用多次的
  7. then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值
*/


const PENDING = 'pending' // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected' // 失败

class myPromise {
  // constructor会在 new myPromise() 的时候自动执行，可以接收参数
  // 这个接受的是一个function 有两个参数，参数也是两个function
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }

  // 定义一个初始状态
  status = PENDING
  // 成功之后的值
  value = undefined
  // 失败之后的原因
  reason = undefined
  // 成功回调
  successCallback = []
  // 失败回调
  failCallback = []

  // resolve接收一个 成功后的value
  resolve = (value) => {
    // 如果状态不是等待，阻止程序向下执行
    if (this.status !== PENDING) return
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存当前promise的value
    this.value = value;
    // 判断成功回调是否存在，如果存在 就调用 存为数组是为了添加多次调用
    while (this.successCallback.length) {
      // shift 从数组中删除一个元素，返回被删除的元素
      this.successCallback.shift()(this.value)
    }
  }
  // reject接收一个 失败后的reason信息
  reject = (reason) => {
    // 如果状态不是等待，阻止程序向下执行
    if (this.status !== PENDING) return
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存当前promise的reason信息
    this.reason = reason;
    // 判断失败回调是否存在，如果存在 就调用 存为数组是为了添加多次调用
    while (this.failCallback.length) {
      // shift 从数组中删除一个元素，返回被删除的元素
      this.failCallback.shift()(this.reason)
    }
  }

  // 接收两个参数 回调函数(function) 成功的函数-失败的函数
  then(successCallback, failCallback) {
    // 看完随堂测试后，觉得应该加上判断是否为一个funtion
    // promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log)
    successCallback = successCallback && typeof successCallback == 'function' ? successCallback : value => value;
    failCallback = failCallback && typeof failCallback == 'function' ? failCallback : reason => { throw reason };
    let newPromise = new myPromise((resolve, rejected) => {
      // 判断状态 执行当前状态对应的回调函数
      // 回调函数接收接收当前状态的value或者错误信息
      // 定义变量 value reason 就是在此处用的---
      if (this.status === FULFILLED) {
        // setTimeout(() => {
        //   try {
        //     let x = successCallback(this.value);
        //     // 判断x的值是普通值还是promise对象
        //     // 如果是普通值，直接调用resolve
        //     // 如果是promise对象，查看promise对象返回的结果
        //     // 再根据promise对象返回的结果，决定调用resolve还是rejected
        //     resolePromise(x, resolve, rejected)
        //   } catch (e) {
        //     rejected(e)
        //   }
        // }, 0)
        handelTask.call(this, successCallback, this.value, resolve, rejected)

      } else if (this.status === REJECTED) {
        // setTimeout(() => {
        //   try {
        //     let x = failCallback(this.reason);
        //     // 判断x的值是普通值还是promise对象
        //     // 如果是普通值，直接调用resolve
        //     // 如果是promise对象，查看promise对象返回的结果
        //     // 再根据promise对象返回的结果，决定调用resolve还是rejected
        //     resolePromise(x, resolve, rejected)
        //   } catch (e) {
        //     rejected(e)
        //   }
        // }, 0)
        handelTask.call(this, failCallback, this.reason, resolve, rejected)
      } else {
        // 等待 --- 假如存在一个异步，解决异步操作
        this.successCallback.push(() => {
          // setTimeout(() => {
          //   try {
          //     let x = successCallback(this.value);
          //     // 判断x的值是普通值还是promise对象
          //     // 如果是普通值，直接调用resolve
          //     // 如果是promise对象，查看promise对象返回的结果
          //     // 再根据promise对象返回的结果，决定调用resolve还是rejected
          //     resolePromise(x, resolve, rejected)
          //   } catch (e) {
          //     rejected(e)
          //   }
          // }, 0)
          handelTask.call(this, successCallback, this.value, resolve, rejected)
        }); // ----回resolve方法中
        this.failCallback.push(() => {
          // setTimeout(() => {
          //   try {
          //     let x = failCallback(this.reason);
          //     // 判断x的值是普通值还是promise对象
          //     // 如果是普通值，直接调用resolve
          //     // 如果是promise对象，查看promise对象返回的结果
          //     // 再根据promise对象返回的结果，决定调用resolve还是rejected
          //     resolePromise(x, resolve, rejected)
          //   } catch (e) {
          //     rejected(e)
          //   }
          // }, 0)
          handelTask.call(this, failCallback, this.reason, resolve, rejected)
        }); // ----回reject方法中
      }
    })
    return newPromise;
  }

  finally(callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }

  catch(failCallback) {
    return this.then(undefined, failCallback)
  }

  static all(array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          // promise 对象
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          // 普通值
          addData(i, array[i]);
        }
      }
    })
  }

  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }
}

function resolePromise(x, resolve, rejected) {
  if (x instanceof myPromise) {
    // promise对象
    // value.then(value=>resolve(resolve), reason=>resolve(reason))
    // 简写
    x.then(resolve, rejected)
  } else {
    // 普通值
    resolve(x)
  }
}


// 提取一下 感觉还是有点不对===========
function handelTask(callBack, info, resolve, rejected) {
  setTimeout(() => {
    try {
      let x = callBack(info);
      // 判断x的值是普通值还是promise对象
      // 如果是普通值，直接调用resolve
      // 如果是promise对象，查看promise对象返回的结果
      // 再根据promise对象返回的结果，决定调用resolve还是rejected
      resolePromise(x, resolve, rejected)
    } catch (e) {
      rejected(e)
    }
  }, 0)
}

module.exports = myPromise